package com.hooper.cmn.service.impl;

import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hooper.cmn.listener.EasyExcelListener;
import com.hooper.cmn.mapper.DictMapper;
import com.hooper.cmn.service.DictService;
import com.hooper.cmn.vo.DictImportVO;
import com.hooper.cmn.vo.DictVO;
import com.hooper.hosp.model.cmn.Dict;
import com.hooper.hospital.common.exception.HospitalException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.hooper.hospital.common.constant.DictConstant.DICT_CODE_PROVINCE;
import static com.hooper.hospital.common.constant.DictConstant.EXCEL_FILE_TYPE;
import static com.hooper.hospital.common.result.ResultCodeEnum.*;

/**
 * @author Tim Hooper
 * @version 1.0
 * @time 2022/09/26/19:43
 */
@Service
@Slf4j
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService {

    @Cacheable(value = "dict", key = "'dictList'+ #id")
    @Override
    public List<Dict> findChildrenById(Long id) {
        Dict dict = this.getById(id);
        if (dict == null) {
            throw new HospitalException(NO_SUCH_DATA);
        }

        LambdaQueryWrapper<Dict> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Dict::getParentId, id);
        List<Dict> list = this.list(wrapper);
        if (list == null) {
            throw new HospitalException(DATA_ERROR);
        }
        //check whether the child has children
        return list.stream().peek(item -> {
            Long childId = item.getId();
            item.setHasChildren(hasChildren(childId));
        }).collect(Collectors.toList());
    }

    @Override
    public boolean downloadDict(HttpServletResponse response) {

        String fileName = "dict.xlsx";

        List<Dict> list = this.list(null);

        List<DictVO> voList = list.stream().map(dict -> {
            DictVO dictVO = new DictVO();
            BeanUtils.copyProperties(dict, dictVO);
            return dictVO;
        }).collect(Collectors.toList());

        try {
            response.setHeader("Content-Disposition",
                    "attachment; filename=\"" + java.net.URLEncoder.encode(fileName, "UTF-8"));
            EasyExcel.write(response.getOutputStream(), DictVO.class).sheet("dict").doWrite(voList);
        } catch (Exception e) {
            throw new HospitalException(e.getMessage(), 500);
        }

        return true;
    }

    @Override
    @CacheEvict(value = "dict", allEntries = true)
    public boolean importDict(MultipartFile file) {

        if (file == null) {
            throw new HospitalException(FILE_NOT_FOUND);
        }
        String filename = file.getOriginalFilename();
        if (StringUtils.isEmpty(filename)){
            throw new HospitalException(DATA_ERROR);
        }
        String fileType = filename.substring(filename.lastIndexOf('.'));
        if (!fileType.equalsIgnoreCase(EXCEL_FILE_TYPE)) {
            throw new HospitalException(FILE_TYPE_NOT_ALLOW);
        }
        try {
            EasyExcel.read(file.getInputStream(), DictImportVO.class, new EasyExcelListener(this.baseMapper)).sheet().doRead();
        } catch (IOException ioException) {
            ioException.printStackTrace();
            return false;
        }
        return true;
    }

    @Override
    public String getDictName(String dictCode, String value) {
        if (StringUtils.isEmpty(value)) {
            throw new HospitalException(PARAM_EMPTY);
        }
        if (StringUtils.isEmpty(dictCode)) {
            LambdaQueryWrapper<Dict> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Dict::getValue, value);
            return baseMapper.selectOne(wrapper).getName();
        }

        //get the dict id which is the parent id of the subordinates
        Long id = this.getByDictCode(dictCode).getId();

        //using the parent id and its value to get the specific dict obj
        LambdaQueryWrapper<Dict> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Dict::getParentId, id)
                .eq(Dict::getValue, value);
        //return the name
        return baseMapper.selectOne(wrapper).getName();
    }

    @Override
    public List<Dict> getSubNode(String dictCode) {
        //get top node id
        Long id = this.getByDictCode(dictCode).getId();

        //query all the sub nodes belong the dict code
        return this.findChildrenById(id);
    }

    /**
     * get a dict obj by dict code
     *
     * @param dictCode condition
     * @return return dict obj
     */
    public Dict getByDictCode(String dictCode) {
        if (StringUtils.isEmpty(dictCode)) {
            throw new HospitalException(PARAM_EMPTY);
        }
        //get top node id by dict code
        LambdaQueryWrapper<Dict> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Dict::getDictCode, dictCode);
        Dict dict = baseMapper.selectOne(wrapper);
        //check if db exists this data (avoid NPE)
        if (dict == null) {
            throw new HospitalException(NO_SUCH_DATA);
        }
        return dict;
    }

    /**
     * check if the children have their own children
     *
     * @param id
     * @return
     */
    public boolean hasChildren(Long id) {
        LambdaQueryWrapper<Dict> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Dict::getParentId, id);
        return this.count(wrapper) > 0;
    }


}
